<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>

<body>
	<div id="app">
		<input type="text" v-model="msg">
		<h3>{{ msg }}</h3>
		<h3>{{user}}</h3>
	</div>
	<script>
		function LcVue(options = {}) {
			this.$options = options;
			// 获取el元素
			this.$el = document.querySelector(options.el)
			this._data = options.data;
			// 设置订阅池进行保存订阅器
			this._watcherTpl = {}
			// 设置obserser函数，对data数据进行重写，实现数据变化的监听
			this._observer(this._data)
			// 编译模板和指令，生成订阅器发布订阅
			this._compile(this.$el)
		}

		LcVue.prototype._observer = function (obj) {
			Object.keys(obj).forEach((key) => {
				// 添加每一个属性的订阅器的存放位置
				this._watcherTpl[key] = {
					_directives: []
				}
				// 方便调用，用一个变量来操作
				let watcherTpl = this._watcherTpl[key];
				// 获取当前key的值
				var value = obj[key];
				Object.defineProperty(this._data, key, {
					configurable: true,
					enumerable: true,
					get() {
						console.log(`${key}获取的值是${value}`)
						return value;
					},
					set(newValue) {
						// 判断赋值的内容是否与原先不一样
						if (newValue !== value) {
							value = newValue;
							// 将订阅池里相对应属性进行触发更新
							console.log("将订阅池里相对应属性进行触发更新")
							watcherTpl._directives.forEach(item => {
								item.update()
							})
						}
					}
				})


			})
		}

		function Watcher(el, vm, val, attr) {
			this.el = el;
			this.vm = vm;
			this.val = val;
			this.attr = attr;
			this.update()
		}

		Watcher.prototype.update = function () {
			this.el[this.attr] = this.vm._data[this.val]
		}

		LcVue.prototype._compile = function (el) {
			// console.log(el)
			var nodes = el.children;
			var len = nodes.length;
			for (var i = 0; i < len; i++) {
				var node = nodes[i];
				if (node.children.length) {
					// 进行递归遍历
					this._compile(node)
				}

				// 判断是否有v-model指令并且是输入框
				if (node.hasAttributes('v-model') && node.tagName === "INPUT") {
					node.addEventListener('input', ((key) => {
						var attVal = node.getAttribute('v-model')
						// 创建watcher对象，并且将订阅器根据属性放入对应的订阅器集合里
						var watcher = new Watcher(node, this, attVal, "value");
						this._watcherTpl[attVal]._directives.push(watcher)
						return () => {
							this._data[attVal] = nodes[key].value
						}
					})(i))
				}

				// 匹配{{}}模板变量
				var reg = /\{\{\s*(.*?)\s*\}\}/igs;
				var txt = node.textContent;
				if (reg.test(txt)) {
					node.textContent = txt.replace(reg, (matched, placeholder) => {
						let attVal = placeholder;
						var watcher = new Watcher(node, this, attVal, "innerHTML");
						this._watcherTpl[attVal]._directives.push(watcher);

						return placeholder.split('.').reduce((val, key) => {
							return val[key]
						}, this._data)
					})
				}

			}
		}


	</script>
	<script>
		var app = new LcVue({
			el: "#app",
			data: {
				msg: "helloworld",
				user: "老陈打码"
			}
		})
	</script>
</body>

</html>